////////////////////////////////////////////////
// DESCRIPTION:
//     Class implementationn for device driver
//     access to Interrupt Request info.
//
// Legal Notices:
//     Copyright (c) 2008, Telliam Consulting, LLC.
//     All rights reserved.
//
//     Redistribution and use in source and binary forms, with or without modification,
//     are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//
//     * Redistributions in binary form must reproduce the above copyright notice,
//       this list of conditions and the following disclaimer in the documentation
//       and/or other materials provided with the distribution.
//
//     * Neither the name of Telliam Consulting nor the names of its contributors
//       may be used to endorse or promote products derived from this software
//       without specific prior written permission.
//
// Disclaimer:
//     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
//     ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
//     WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//     IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
//     INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
//     LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
//     OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
//     OF THE POSSIBILITY OF SUCH DAMAGE.  
//
#include <windows.h>
#include <devload.h>
#include <winerror.h>
#include <pkfuncs.h>
#include <nkintr.h>
#include <RESMGR.H>
#include <FusionWare\DFX.h>
#include <FusionWare\Win32.h>

using namespace FusionWare;
using namespace FusionWare::DFX;
using namespace FusionWare::Win32;

IrqInfo::IrqInfo()
{
    DDKIsrInfo.cbSize = sizeof(DDKIsrInfo);
    DDKIsrInfo.dwIrq = IRQ_UNSPECIFIED;
    DDKIsrInfo.dwSysintr = SYSINTR_UNDEFINED ;
    DDKIsrInfo.szIsrDll[0]=0;
    DDKIsrInfo.szIsrHandler[0]=0;

    this->pIsrInfo = NULL;
    this->IsrInfoSize = 0;

    this->IrqMode = None;
    this->IrqState = 0;
    this->EnableWake = FALSE;
    this->Priority256 = -1;
    this->SysIntrRequested = false;
}

IrqInfo::~IrqInfo()
{
    delete this->pIsrInfo;
}

bool IrqInfo::StartIST(FusionWare::Threads::IntSvcThread::IntSvcThreadFunction Func, DWORD DefaultPriority)
{
    // set a default priority if none provided in the registry
    if(this->Priority256 >= 256)
    {
        ASSERT(DefaultPriority < 256);
        this->Priority256 = DefaultPriority; 
    }

    InitIrq();

    // Hook up the IST to the SYSINTR
    if(!this->ISTObj.Init(GetSysIntr()))
        return false;

    return this->ISTObj.Start(Func, this->Priority256);
}

bool IrqInfo::StopIST()
{
    return this->ISTObj.Stop();
}

bool IrqInfo::ReadRegValues(RegKey& RootKey)
{
    DWORD valSize = 0;
    
    DDKIsrInfo.szIsrDll[0] = TCHAR('\0');
    DDKIsrInfo.szIsrHandler[0] = TCHAR('\0');

    if(!RegValues::ReadRegValues(RootKey))
        return false;
    
    // If ISR DLL is listed then get the additional "Blob"
    // of data to provide to the ISR
    //
    if(DDKIsrInfo.szIsrDll[0]!=TCHAR('\0'))
    {
        if(DDKIsrInfo.szIsrHandler[0]==TCHAR('\0'))
        {
            // IsrHandler is required if an IsrDLL is used
            SetLastError(ERROR_INVALID_DATA);
            return FALSE;
        }

        valSize = RootKey.QueryValueSize(_T("IrqInfo"));
        this->pIsrInfo = malloc(valSize);
        if(NULL == this->pIsrInfo)
        {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return false;  
        }

        if(!RootKey.ReadValue(_T("IrqInfo"), this->pIsrInfo, valSize))
            return false;

        this->IsrInfoSize = valSize;
    }

    return true;
}

bool IrqInfo::RequestSysIntr()
{
    if(this->SysIntrRequested)
        return true;

    if(  DDKIsrInfo.dwIrq != IRQ_UNSPECIFIED
      && DDKIsrInfo.dwSysintr == SYSINTR_UNDEFINED)
    {
        this->SysIntrRequested = DFX::RequestSysIntr(DDKIsrInfo.dwIrq, &DDKIsrInfo.dwSysintr);
        return this->SysIntrRequested;
    }
    return true;
} 

bool IrqInfo::ReleaseSysIntr()
{
    if(!this->SysIntrRequested)
        return true;

    bool retVal = DFX::ReleaseSysIntr(DDKIsrInfo.dwSysintr);
    
    // only mark it as unallocated if succesfully de-allocated
    this->SysIntrRequested = !retVal; 

    return retVal;
} 

 
bool IrqInfo::SysEnableWake(bool Enable)
{
    // set up the device to wake from suspend if requested
    if(this->EnableWake)
    {
        if(!KernelIoControl(Enable ? IOCTL_HAL_ENABLE_WAKE : IOCTL_HAL_DISABLE_WAKE, &DDKIsrInfo.dwSysintr, sizeof(DDKIsrInfo.dwSysintr),0,0,0))
        {
            ERRORMSG(1,(_T("Change Wake state failed. (0x%X)\r\n"), GetLastError()));
            return false;
        }
    }
    return true;
}

bool IrqInfo::LoadIntChainHandler()
{
    // if there is no ISR DLL then that's OK.
    if(DDKIsrInfo.szIsrDll[0]==TCHAR('\0') || DDKIsrInfo.szIsrHandler[0]==TCHAR('\0'))
        return true;
    
    // don't re-load the handler
    if(this->ChainHandler.IsLoaded())
        return true;

    if(!this->ChainHandler.Load( DDKIsrInfo.szIsrDll
                               , DDKIsrInfo.szIsrHandler
                               , DDKIsrInfo.dwSysintr
                               , DDKIsrInfo.dwIrq
                               , pIsrInfo
                               , IsrInfoSize))
    {
        ERRORMSG(1, (TEXT("IrqInfo::LoadIntChainHandler: Error loading ISR chain handler. (0x%X)\r\n"), GetLastError()));
        return false;
    }

    return true;
}

bool IrqInfo::InitIrq()
{
    // request a SYSINTR if not already provided
    if(!RequestSysIntr())
    {
        DEBUGMSG(TRUE, (_T("IrqInfo::IntIrq: RequestSysIntr failed! (0x%X)\r\n"), GetLastError()));
        return false;
    }

    if(!LoadIntChainHandler())
        return false;

    // set the IRQ mode for the device based on registry settings
    if(!SetIrqMode())
    {
        DEBUGMSG(TRUE, (_T("IrqInfo::IntIrq: SetIrqMode failed!\r\n")));
        return false;
    }

    // set up the device to wake from suspend if requested
    // This only returns FALSE if the wake is requested but it failed to set
    // the wake state. All other cases return TRUE.
    if(!SysEnableWake())
    {
        ERRORMSG(1, (TEXT("IrqInfo::IntIrq: Error Setting device wake source. (0x%X)\r\n"), GetLastError()));
        return false;
    }

    return true;
}


